#pragma once
#include "sqlite3.h"
#define DB_NAME "tiku.db"
#include <string>
#include <iostream>
#include <cstdio>
#include <map>
#include <algorithm>
#include <cstring>
#include <stdexcept>
using namespace std;

struct search_result {
    string q;
    string a;
    double similarity;
    search_result() {}
    search_result(string _q, string _a, double _s) : q{_q}, a{_a}, similarity{_s} {}
};

// g++ .\tiku.cpp -I D:/mingw64/opt/include -L D:/mingw64/opt/lib -lsqlite3
inline int callback_1(void *arg, int argc, char **argv, char **colname) {
    map<string, string>* quesiotns = static_cast<map<string, string>*>(arg);
    if (argc == 2) {
        // if (strlen(argv[1]) == 0) return -1;
        // cout << argv[0] << " " << argv[1] << endl;
        quesiotns->insert(std::pair<string, string>(argv[0], argv[1]));
    }
    return 0;
}

inline int min_edit_distance(const string& a, const string& b) {
    int n = a.length();
    int m = b.length();
    int dp[n + 1][m + 1];
    for (int i = 0; i <= n; i++) {
        dp[i][0] = i;
    }
    for (int i = 0; i <= m; i++) {
        dp[0][i] = i;
    }
    for (int i = 1; i < n + 1; i++) {
        for (int j = 1; j < m + 1; j++) {
            if (a[i - 1] == b[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1];
            } else {
                dp[i][j] = min(dp[i - 1][j], min(dp[i][j - 1], dp[i - 1][j - 1])) + 1;
            }
        }
    }
    return dp[n][m];
}

class Tiku {
    sqlite3* pdb;
    string _dbmame;
    map<string, string> questions;
    char* errmsg;
public:
    Tiku(const char* dbname) : _dbmame{dbname} {
        int r = sqlite3_open(DB_NAME, &pdb);
        if (r != SQLITE_OK) {
            throw std::runtime_error("open %s failed");
        }
        fetchAll();
        cout << "***************\n";
        cout << "init tiku success!\n";
        cout << "***************\n";
    }

    Tiku(string dbname) : _dbmame{dbname} {
        int r = sqlite3_open(DB_NAME, &pdb);
        if (r != SQLITE_OK) {
            cout << "***************\n";
            throw std::runtime_error("open %s failed");
        }
        fetchAll();
        cout << "init tiku success!\n";
    }
    ~Tiku() {
        sqlite3_close(pdb);
    }
    void fetchAll() {
        int r = sqlite3_exec(pdb, "select * from tiku;", callback_1, (void*)&questions, &errmsg);
        if (r != SQLITE_OK) {
            cout << errmsg << endl;
            return;
        }
    }

    search_result search(string q) {
        search_result ret;
        if (q.length() <= 2) {
            cout << "重新截取图片" << endl;
            return search_result{"重新截取图片", "重新截取图片", 0};
        }
        auto pair_ = questions.find(q);
        if (pair_ != questions.end()) {
            ret.q = pair_->first;
            ret.a = pair_->second;
            ret.similarity = 100;
            return ret;
        }
        int min_distance = INT_MAX;
        int i = 0;
        std::pair<string, string> best_match;
        for (auto question : questions) {
            int distance = min_edit_distance(q, question.first);
            if (distance == 0) {
                min_distance = 0;
                break;
            }
            i++;
            if (distance < min_distance) {
                min_distance = distance;
                best_match = question;
            }
            // cout << distance << endl;
        }
        double similarity = 1 - (min_distance * 1.0) / (max(q.length(), best_match.first.length()));
        // cout << "min_distance = " << min_distance << endl;
        cout << best_match.first << " " << best_match.second << ", 相似度: " << (similarity * 100) << "%" << endl;
        ret.q = best_match.first;
        ret.a = best_match.second;
        ret.similarity = similarity;
        if (similarity < 0.75) {
            ret.q = "未找到";
            insert_v2(q);
        }
        return ret;
    }

    void insert(string q, string a = "null") {
        string sql = "insert into tiku(`题目`,`答案`) values('" + q + "','" + a + "');";
        cout << sql << endl;
        int r = sqlite3_exec(pdb, sql.c_str(), nullptr, nullptr, &errmsg);
        if (r != SQLITE_OK) {
            cerr << errmsg << endl;
        }
    }

    void insert_v2(string q, string a = "null") {
        string sql = "insert into tiku(`题目`,`答案`) values(@q,@a);";
        sqlite3_stmt* pstmt;
        int r = sqlite3_prepare_v2(pdb, sql.c_str(), -1, &pstmt, nullptr);
        if (r != SQLITE_OK) {
            cerr << sqlite3_errmsg(pdb);
            return;
        }
        sqlite3_bind_text(pstmt, 1, q.c_str(), q.length(), nullptr);
        sqlite3_bind_text(pstmt, 2, a.c_str(), a.length(), nullptr);
        r = sqlite3_step(pstmt);
        if (r != SQLITE_DONE) {
            cerr << sqlite3_errmsg(pdb) << endl;
        }
        sqlite3_finalize(pstmt);
    }

    void update(string q, string a = "对") {
        string sql = "update tiku set `答案` = " + a + "where `题目` = " + q;
        int r = sqlite3_exec(pdb, sql.c_str(), nullptr, nullptr, &errmsg);
        if (r != SQLITE_OK) {
            cerr << errmsg << endl;
        }
    }

    void remove(string q) {
        string sql = "delete from tiku where `题目` = " + q;
        int r = sqlite3_exec(pdb, sql.c_str(), nullptr, nullptr, &errmsg);
        if (r != SQLITE_OK) {
            cerr << errmsg << endl;
        }
    }

    void remove() {
        string sql = "delete from tiku where `答案`= 'null';";
        int r = sqlite3_exec(pdb, sql.c_str(), nullptr, nullptr, &errmsg);
        if (r != SQLITE_OK) {
            cerr << errmsg << endl;
        }
    }

    void show() {
        for (auto q : questions) {
            cout << q.first << " " << q.second << endl;
        }
    }

    map<string, string>& getQuestions() {
        return questions;
    }
};
